home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 2
/
Nebula Two.iso
/
SourceCode
/
GameKit
/
Examples
/
PacMan
/
Monster.m
< prev
next >
Wrap
Text File
|
1995-06-12
|
10KB
|
350 lines
#import <libc.h>
#import "Monster.h"
#import "PacManGameBrain.h"
#import "Player.h"
#import "Maze.h"
#import <appkit/appkit.h>
// Get defs of static data arrays which control movement choices
#import "MonsterMovement.h"
@implementation Monster
- init
{
return [self initGhost:0 player:nil maze:nil at:0 :0];
}
- initGhost:(int)num player:(id)pac maze:(id)world at:(int)sx :(int)sy
{ // initialize all instance vars
[super init];
ghosts[1] = [NXImage findImageNamed:"Ghosts.tiff"];
ghosts[2] = [NXImage findImageNamed:"GhostsBig.tiff"];
lastx = - 4 * GHOST_SIZE;
lasty = - 4 * GHOST_SIZE;
player = pac;
maze = world;
eatable = NO;
state = HOVER;
myX = sx * GHOST_SIZE; myY = sy * GHOST_SIZE;
myColor = num;
px = 2; py = 0;
loops = 0;
fix = NO;
return self;
}
- (BOOL)canBeEaten // returns "eatable".
{ return eatable; }
- move:sender // move one frame
{
// use current state to decide which type of movement to do
switch (state) {
case HOVER : {
[self hover];
break;
}
case CHASE : {
[self chase];
break;
}
case RUN : {
[self runAway];
break;
}
case HOME : {
[self goHome];
break;
}
default : { // no movement, since illegal state
// (we should _never_ get here!)
px = 0; py = 0;
break;
}
}
return self;
}
- hover // hover moves solid ghost when in ghost chamber
{
int door_x, door_y;
int k;
[maze doorPosition:&door_x :&door_y];
if (myX == door_x) {
if (myY >= (door_y + GHOST_SIZE)) {
// The ghost is now completely outside the box; we will
// change its state so that it follows the player around
// (or runs away if power dot was eaten)
if (eatable) {
state = RUN;
[self runAway];
} else {
state = CHASE;
[self chase];
}
if (myY == (door_y + GHOST_SIZE - 1)) py = 1;
if (myY == (door_y + GHOST_SIZE + 1)) py = -1;
return self;
} else if (myY >= (door_y - 1 - GHOST_SIZE)) {
// The ghost is directly underneath the door to the outside.
// Send it out if more than 3 loops made, or 50% chance to leave if
// at least one loop is complete.
if ((++loops) > 1) {
if ((loops > 3) || ((random() & 0x0f) > 7) || eatable) {
if (eatable) py = 1; // keep right speed
else py = 2;
px = 0;
return self;
} } } }
// The rest of the method drives the ghost around the
// box in a counterclockwise pattern.
k = 0;
if (px > 0) {
if ([maze monsterWall:(myX + 1 + GHOST_SIZE) :myY])
{ k = 1; px = 0; py = 2; }
} else if (px < 0) {
if ([maze monsterWall:(myX - 2) :myY])
{ k = 1; px = 0; py = -2; }
} else if (py > 0) {
if ([maze monsterWall:myX :(myY + 1 + GHOST_SIZE)])
{ k = 1; px = -2; py = 0; }
} else if ([maze monsterWall:myX :(myY - 2)])
{ k = 1; px = 2; py = 0; }
if (eatable && k) {
px /= 2;
py /= 2;
}
if ((abs(py) == 2) && (myY & 0x1)) fix = YES; // keep synced
if ((abs(px) == 2) && (myX & 0x1)) fix = YES; // keep synced
return self;
}
- runAway // ghost runs away at 1/2 speed after power pill
{
int pac_x = [player xpos];
int pac_y = [player ypos];
register int tdir = 0x0f, sense;
int dx = myX % GHOST_SIZE; int dy = myY % GHOST_SIZE;
if (dx) tdir &= 0x03; // only allow l/r to sync us up
if (dy) tdir &= 0x0c; // only allow u/d to sync us up
// first, find the directions in which this ghost can go
if ([maze playerWall:(myX+GHOST_SIZE) :myY] || (px < 0)) tdir &= ~0x01;
if ([maze playerWall:(myX-1) :myY] || (px > 0)) tdir &= ~0x02;
if ([maze playerWall:myX :(myY-1)] || (py > 0)) tdir &= ~0x04;
if ([maze playerWall:myX :(myY+GHOST_SIZE)] || (py < 0)) tdir &= ~0x08;
// now choose the new direction for the ghost
if ((random() & 0x0f) > 4)
sense = find[sgn(pac_y - myY) + 1][sgn(pac_x - myX) + 1];
else sense = random() & 0x07;
px = rxvec[tdir][sense];
py = ryvec[tdir][sense];
return self;
}
- chase // ghost chases player down
{
int pac_x = [player xpos];
int pac_y = [player ypos];
register int tdir = 0x0f;
long rnum = random() & 0x3FF;
register BOOL noReverse = (rnum != 1);
register int sense, curPath;
int dx = myX % GHOST_SIZE; int dy = myY % GHOST_SIZE;
// sync position to block boundaries
if (px < 0) {
if (dx > 1) return self;
if (dx == 1) { px = -1; return self; }
} else if (px > 0) {
if ((dx < GHOST_SIZE - 1) && dx) return self;
if (dx == GHOST_SIZE - 1) { px = 1; return self; }
} else if (py < 0) {
if (dy > 1) return self;
if (dy == 1) { py = -1; return self; }
} else if (py > 0) {
if ((dy < GHOST_SIZE - 1) && dy) return self;
if (dy == GHOST_SIZE - 1) { py = 1; return self; }
}
// determine where the pac is at in relation to us.
curPath = find[sgn(pac_y - myY) + 1][sgn(pac_x - myX) + 1];
// we don't use random sense if ghost can reverse,
// since it ends up looking dumb
if (((random() & 0x0f) > 4) || !noReverse) // 75% of time it's algorithmic
sense = curPath;
else sense = random() & 0x07;
// first, find the directions in which this ghost can go
if ([maze playerWall:(myX+2+GHOST_SIZE) :myY] || ((px < 0) && noReverse))
tdir &= ~0x01;
if ([maze playerWall:(myX-2) :myY] || ((px > 0) && noReverse))
tdir &= ~0x02;
if ([maze playerWall:myX :(myY-2)] || ((py > 0) && noReverse))
tdir &= ~0x04;
if ([maze playerWall:myX :(myY+2+GHOST_SIZE)] || ((py < 0) && noReverse))
tdir &= ~0x08;
// now choose the new direction for the ghost
px = fxvec[tdir][sense];
py = fyvec[tdir][sense];
return self;
}
- goHome // used to make the eyes run to ghost chamber at 2x speed
{
int door_x, door_y;
register int tdir = 0x0f, sense;
int dx = myX % GHOST_SIZE; int dy = myY % GHOST_SIZE;
[maze doorPosition:&door_x :&door_y];
// there, we'll "catch" it and suck it into the box.
if (myX == door_x) { // using GHOST_SIZE * 2 moves it to bottom of box
if ((myY >= door_y - GHOST_SIZE * 2) && (myY <= door_y + GHOST_SIZE)) {
if ((myY >= door_y - GHOST_SIZE) && (myY <= door_y + GHOST_SIZE)) {
// The ghost is right above the door to the ghost box.
// We'll send it down into the box. We're assuming
// here that the ghost box is below the door.
// If not, the results will be unpredictable.
px = 0;
if (dy && (dy < 4)) py = -dy; // sync to blocks
else py = -4;
} else if (myY == (door_y - GHOST_SIZE * 2)) {
// The ghost is all the way inside the box. Here it'll
// be "reborn" -- its state will be changed to that of a
// solid ghost hovering inside the ghost box.
state = HOVER;
loops = 0;
eatable = NO;
px = 2;
py = 0;
}
return self;
} }
door_y += GHOST_SIZE; // seek the block above the door; when ghost gets
// now, sync ghost to maze blocks
if (px < 0) {
if (dx > 3) return self;
if (dx) { px = -dx; return self; }
} else if (px > 0) {
if ((dx < GHOST_SIZE - 3) && dx) return self;
else if (dx) { px = GHOST_SIZE - dx; return self; }
} else if (py < 0) {
if (dy > 3) return self;
if (dy) { py = -dy; return self; }
} else if (py > 0) {
if ((dy < GHOST_SIZE - 3) && dy) return self;
else if (dy) { py = GHOST_SIZE - dy; return self; }
}
if (dx) tdir &= 0x03;
if (dy) tdir &= 0x0c;
// first, find the directions in which this ghost can go
if ([maze monsterWall:(myX+4+GHOST_SIZE) :myY] || (px < 0)) tdir &= ~0x01;
if ([maze monsterWall:(myX-4) :myY] || (px > 0)) tdir &= ~0x02;
if ([maze monsterWall:myX :(myY-4)] || (py > 0)) tdir &= ~0x04;
if ([maze monsterWall:myX :(myY+4+GHOST_SIZE)] || (py < 0)) tdir &= ~0x08;
// now choose the new direction for the ghost--not random!
sense = find[sgn(door_y - myY) + 1][sgn(door_x - myX) + 1];
px = pxvec[tdir][sense];
py = pyvec[tdir][sense];
return self;
}
- powerDot:(BOOL)eat // called when power dots are eaten or run out.
{
if (state != HOME) eatable = eat;
if ((state == CHASE) && (eat == YES)) {
state = RUN;
px = 0; py = 0; // allows quick reverse of direction
[self runAway];
}
if ((state == RUN) && (eat == NO)) {
state = CHASE;
if (abs(px) < 2) px *= 2; // ifs should actually be unnecessary...
if (abs(py) < 2) py *= 2;
[self chase];
}
if (state == HOVER) { // speed up or slow down hovering ghost
// has to happen here, since hover leaves it alone until it hits
// an edge of the box...
if (eat) {
if (abs(px) == 2) px /=2;
if (abs(py) == 2) py /= 2;
} else {
if (abs(px) == 1) px *=2;
if (abs(py) == 1) py *= 2;
} }
if (eat) {
powerState = DOT_EATEN;
powerTime = 0;
} else powerState = DOT_NONE;
return self;
}
- (int)munch // called when pac intersects ghost
{
if (state == HOME) return HARMLESS;
if (!eatable) return NO;
px = 0; py = 0; // allows reverse of direction
state = HOME;
[self goHome];
eatable = NO;
return YES;
}
- moveOneFrame
{
[super moveOneFrame]; // move the ghost
if (fix) {
myY -= sgn(py); // fix for when hovering -- to re-sync
myX -= sgn(px);
fix = 0;
}
return self;
}
- renderAt:(int)posx :(int)posy move:(BOOL)moveOk // draw ghost
{
int col = myColor & 0x3;
NXRect from;
NXPoint pos;
[super renderAt:posx :posy move:moveOk];
// decide to draw invisible or eye ghosts if necessary
if (eatable && ((powerState != DOT_FADING) || (img & 0x4)))
col = INVISIBLE_GHOST;
else if (state == HOME) col = GHOST_EYES;
NXSetRect(&from,
((++img >> 1) & 0x3) * GHOST_SIZE * scale, col * GHOST_SIZE * scale,
GHOST_SIZE * scale, GHOST_SIZE * scale);
pos.x = myX * scale + posx; pos.y = myY * scale + posy;
[ghosts[scale] composite:NX_SOVER fromRect:&from toPoint:&pos];
[self powerCount]; // count down time till power dot wears off
return self;
}
- powerCount
{
if (![[NXApp delegate] paused]) {
if (((++powerTime) >> 4) > flash_ticks[[[NXApp delegate] level]])
powerState = DOT_FADING;
if ((powerTime >> 4) > off_ticks[[[NXApp delegate] level]])
[self powerDot:NO];
}
return self;
}
@end